#include "../classifier_test/all.h"
#include "../classifier/c45r8/all.h"
#include "../classifier/svm_light/all.h"

using namespace classifier_test;

command_line_options_handler Handler;
class_factory ClassFactory;
string StreamFilename;
string ModelFilename;
int SkipCount, TestCount;
smart_ptr<classifier_tester> Tester;

void init_handler(int argc, char* argv[])
{
	Handler
		.add_usage("[options] <stream_file>.data [<model_file>]")
		.add_usage("--help");
	Handler.options()
		.add("help").set_info("Display this information")
		.add("verbose").set_argument("{0,1,3,5}").set_info("Details of trace information, default 3")
		.add("skip").set_argument("<count>").set_info("Skip the first <count> records, default 0")
		.add("test").set_argument("<count>").set_info("# of records for testing, default -1 for all");
	Handler.set_arguments(argc, argv);
	Handler.set_front_info("Input stream classifiers from model file and test on specified stream file");
	Handler.set_back_info(
		"Example:\n"
		"  " + Handler.get_program_name() + " stagger.data\n"
		"  " + Handler.get_program_name() + " --skip=100000 --test=100000 stagger.data\n"
		"This program is written by Shixi Chen <xreborner@163.com>");
}

void process_arguments(const command_line_arguments& Args)
{
	if (Args.empty())
	{
		Handler.show_usages();
		exit(0);
	}
	if (!Args["help"].empty())
	{
		Handler.show_help();
		exit(0);
	}

	clogger()->set_level(logger::level(logger::warning_level - to_int(Args["verbose"].get_or_default("3"))));
	StreamFilename = Args[""][0];
	if (Args[""].size() < 2) ModelFilename = "model.txt";
	else ModelFilename = Args[""][1];

	SkipCount = to_int(Args["skip"].get_or_default("0"));
	TestCount = to_int(Args["test"].get_or_default("-1"));
}

int mymain(int argc, char* argv[])
{
	ClassFactory.add<c45r8_classifier>("c45r8");
	ClassFactory.add<svm_light_classifier>("svm-light");
	ClassFactory.add<svm_struct_classifier>("svm-struct");
	ClassFactory.add<high_order_classifier>("high-order");
	ClassFactory.add<vem_classifier>("vem");
	init_handler(argc, argv);
	try
	{
		process_arguments(command_line_arguments(Handler.options(), Handler.parse()));
	}
	catch (command_line_option_error& E)
	{
		checked_printf("%s: %s\n", Handler.get_program_name().c_str(), E.what());
		exit(1);
	}

	clogger()->info("Reading " + ModelFilename);
	Tester.reset(new classifier_tester);
	text_serializer Serializer;
	Serializer.set_class_factory(&ClassFactory);
	Serializer.load(ModelFilename);
	Tester->serialize(Serializer);
	Serializer.close();
	smart_ptr<data_file_stream> Stream(new data_file_stream);
	Stream->set_scheme(Tester->get_scheme());
	Stream->open(StreamFilename, "r");
	Tester->skip(&*Stream, SkipCount);
	Tester->test(&*Stream, TestCount);

	const vector<classifier_tester::test_result>& Results = Tester->get_test_results();

	clogger()->info("Writing test-performance.txt");
	smart_file File("test-performance.txt", "w");
	checked_fprintf(File, "Model file: %s\n", ModelFilename.c_str());
	checked_fprintf(File, "Stream file: %s\n", StreamFilename.c_str());
	checked_fprintf(File, "Skip count: %d\n", SkipCount);
	checked_fprintf(File, "Data count: %d\n", Results[0].data_count);
	checked_fprintf(File, "\n");
	for (int I = 0; I < (int)Tester->get_classifiers().size(); I++)
	{
		checked_fprintf(File, "Stream classifier: %s\n", ClassFactory.get_name(typeid(*Tester->get_classifiers()[I])).c_str());
		checked_fprintf(File, "Test time: %.3lf\n", Results[I].test_time);
		checked_fprintf(File, "Errors: %d\n", Results[I].errors);
		checked_fprintf(File, "Error rate: %.6lf\n", Results[I].get_error_rate());
		checked_fprintf(File, "\n");
	}

	clogger()->info("Writing test-result.txt");
	File.reset("test-result.txt", "w");
	for (int I = 0; I < (int)Tester->get_classifiers().size(); I++)
	{
		checked_fprintf(File, "Stream classifier: %s\n", ClassFactory.get_name(typeid(*Tester->get_classifiers()[I])).c_str());
		Tester->write_classifications(File, I);
		checked_fprintf(File, "\n");
	}

	return 0;
}

int main(int argc, char* argv[])
{
	srand((int)time(0));
	return safe_main(mymain, argc, argv);
}
